﻿using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.SceneManagement;

/* 使用说明：
 * -在EEventType和EventDict里面增加事件
 * -使用AddListener/RemoveListener来监听/取消监听事件
 * -使用BrocastEvent广播事件
 */

namespace Memo.Electricity.Engine
{
	/// <summary>
	/// 在这里定义所有事件
	/// </summary>
	public enum EEventType
	{
		//测试用事件
		OnTest,
		OnTest1,
		OnTest2,

		//场景事件
		OnSceneLoaded,
		OnSceneUnloaded,
		OnActiveSceneChanged,

		//玩家事件
		OnPlayerDied,
		OnPlayerWin

		//新的事件在后面追加，记得用注释分类
	}

	public class CEventMgr : CSingletonBehaviour<CEventMgr>
	{
		//在这里定义事件的参数类型
		private Dictionary<EEventType, Type> mEventDict = new Dictionary<EEventType, Type>()
		{
			//测试用事件 
			{EEventType.OnTest, typeof(Action)},
			{EEventType.OnTest1, typeof(Action<int>)},
			{EEventType.OnTest2, typeof(Action<string, string>)},

			//场景事件
			{EEventType.OnSceneLoaded, typeof(Action<Scene, LoadSceneMode>)},
			{EEventType.OnSceneUnloaded, typeof(Action<Scene>)},
			{EEventType.OnActiveSceneChanged, typeof(Action<Scene, Scene>)},

			//玩家事件
			{EEventType.OnPlayerDied,typeof(Action) },
			{EEventType.OnPlayerWin,typeof(Action) }
			//新的事件在后面追加，记得用注释分类
		};

		#region AddListener

		public void AddListener(EEventType eventType, Action callback)
		{
			OnAddListener(eventType, callback);
			mEvtTable[eventType] = (Action)mEvtTable[eventType] + callback;
		}

		public void AddListener<T1>(EEventType eventType, Action<T1> callback)
		{
			OnAddListener(eventType, callback);
			mEvtTable[eventType] = (Action<T1>) mEvtTable[eventType] + callback;
		}

		public void AddListener<T1, T2>(EEventType eventType, Action<T1, T2> callback)
		{
			OnAddListener(eventType, callback);
			mEvtTable[eventType] = (Action<T1, T2>) mEvtTable[eventType] + callback;
		}

		public void AddListener<T1, T2, T3>(EEventType eventType, Action<T1, T2, T3> callback)
		{
			OnAddListener(eventType, callback);
			mEvtTable[eventType] = (Action<T1, T2, T3>) mEvtTable[eventType] + callback;
		}

		#endregion

		#region RemoveListener

		public void RemoveListener(EEventType eventType, Action callback)
		{
			OnRemoveListener(eventType, callback);
			mEvtTable[eventType] = (Action) mEvtTable[eventType] - callback;
		}

		public void RemoveListener<T1>(EEventType eventType, Action<T1> callback)
		{
			OnRemoveListener(eventType, callback);
			mEvtTable[eventType] = (Action<T1>)mEvtTable[eventType] - callback;
		}

		public void RemoveListener<T1, T2>(EEventType eventType, Action<T1, T2> callback)
		{
			OnRemoveListener(eventType, callback);
			mEvtTable[eventType] = (Action<T1, T2>)mEvtTable[eventType] - callback;
		}

		public void RemoveListener<T1, T2, T3>(EEventType eventType, Action<T1, T2, T3> callback)
		{
			OnRemoveListener(eventType, callback);
			mEvtTable[eventType] = (Action<T1, T2, T3>)mEvtTable[eventType] - callback;
		}

		#endregion

		#region Broadcast Event

		public void Broadcast(EEventType eventType)
		{
			OnBroadCast(eventType, typeof(Action));
			(mEvtTable[eventType] as Action)?.Invoke();
		}

		public void Broadcast<T1>(EEventType eventType, T1 arg1)
		{
			OnBroadCast(eventType, typeof(Action<T1>));
			(mEvtTable[eventType] as Action<T1>)?.Invoke(arg1);
		}

		public void Broadcast<T1, T2>(EEventType eventType, T1 arg1, T2 arg2)
		{
			OnBroadCast(eventType, typeof(Action<T1, T2>));
			(mEvtTable[eventType] as Action<T1, T2>)?.Invoke(arg1, arg2);
		}

		public void Broadcast<T1, T2, T3>(EEventType eventType, T1 arg1, T2 arg2, T3 arg3)
		{
			OnBroadCast(eventType, typeof(Action<T1, T2, T3>));
			(mEvtTable[eventType] as Action<T1, T2, T3>)?.Invoke(arg1, arg2, arg3);
		}

		#endregion

		#region Event Expection

		public class BroadcastException : Exception
		{
			public BroadcastException(string msg)
				: base(msg)
			{
			}
		}

		public class ListenerException : Exception
		{
			public ListenerException(string msg)
				: base(msg)
			{
			}
		}

		#endregion

		private Dictionary<EEventType, Delegate> mEvtTable = new Dictionary<EEventType, Delegate>();

		private void Awake()
		{
			foreach (var key in mEventDict.Keys)
			{
				mEvtTable.Add(key, null);
			}
		}

		private void OnAddListener(EEventType eventType, Delegate callback)
		{
			if (mEventDict[eventType] != callback.GetType())
			{
				throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, mEventDict[eventType].Name, callback.GetType().Name));
			}
		}

		private void OnRemoveListener(EEventType eventType, Delegate callback)
		{
			if (mEventDict[eventType] != callback.GetType())
			{
				throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, mEventDict[eventType].Name, callback.GetType().Name));
			}
		}

		private void OnBroadCast(EEventType eventType, Type actionType)
		{
			if (mEventDict[eventType] != actionType)
			{
				throw new BroadcastException(string.Format("Attempting to broadcast with inconsistent signature for event type {0}. Current listeners have type {1} but you want to broadcast action has type {2}", eventType, mEventDict[eventType].Name, actionType));
			}
		}

	}
}

